﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using NModbus;
using NModbus.Device;

public class ModbusDriver
{
    private readonly Dictionary<string, TcpClient> _tcpClients = new();
    private readonly Dictionary<string, SerialPort> _serialPorts = new();
    private readonly ConcurrentDictionary<string, ushort[]> _lastValues = new();
    private readonly ConcurrentDictionary<string, List<Action<ushort[]>>> _subscribers = new();

    private CancellationTokenSource _cancellationTokenSource;
    private readonly object _configLock = new();
    // 存储设备的配置
    private readonly ConcurrentDictionary<string, DeviceConfig> _deviceConfigs = new();

    // 设备连接的 Modbus 主站（Tcp 或 RTU）
    private readonly ConcurrentDictionary<string, IModbusMaster> _masters = new();

    // 存储定时器对象，用于轮询设备
    private readonly ConcurrentDictionary<string, Timer> _pollingTimers = new();

    // 数据接收事件（可以通过订阅发布模式发送数据）
    public event Action<string, ushort[]> OnDataReceived;

    public event Action<string, Exception> OnConnectionLost;
    public event Action<string> OnReconnected;

    public Action<string> LogInfo { get; set; }
    public Action<string> LogError { get; set; }

    public void LoadConfiguration(IEnumerable<DeviceConfig> configs)
    {
        lock (_configLock)
        {
            foreach (var config in configs)
            {
                _deviceConfigs[config.DeviceId] = config;
            }
        }
    }

    public void UpdateConfiguration(DeviceConfig config)
    {
        lock (_configLock)
        {
            // 停止旧的轮询定时器
            StopPolling(config.DeviceId);
            _deviceConfigs[config.DeviceId] = config;
            // 根据新的配置启动轮询
            StartPolling(config);
        }
    }

    public void RemoveConfiguration(string deviceId)
    {
        lock (_configLock)
        {
            _deviceConfigs.TryRemove(deviceId, out _);
            Disconnect(deviceId);
        }
    }

    public void Start()
    {
        _cancellationTokenSource = new CancellationTokenSource();
        foreach (var config in _deviceConfigs.Values)
        {
            Task.Run(() => StartDevice(config), _cancellationTokenSource.Token);
        }
    }

    public void Stop()
    {
        _cancellationTokenSource?.Cancel();
        lock (_tcpClients)
        {
            foreach (var client in _tcpClients.Values)
            {
                client.Close();
            }
        }
        lock (_serialPorts)
        {
            foreach (var port in _serialPorts.Values)
            {
                port.Close();
            }
        }
        foreach (var deviceId in _pollingTimers.Keys.ToList())
        {
            StopPolling(deviceId);
        }
        _tcpClients.Clear();
        _serialPorts.Clear();
        _masters.Clear();
        _subscribers.Clear();
        _lastValues.Clear();
    }

    public void Subscribe(string deviceId, Action<ushort[]> callback)
    {
        if (!_subscribers.ContainsKey(deviceId))
        {
            _subscribers[deviceId] = new List<Action<ushort[]>>();
        }
        _subscribers[deviceId].Add(callback);
    }

    public void Unsubscribe(string deviceId, Action<ushort[]> callback)
    {
        if (_subscribers.ContainsKey(deviceId))
        {
            _subscribers[deviceId].Remove(callback);
        }
    }

    private async Task StartDevice(DeviceConfig config)
    {
        while (!_cancellationTokenSource.Token.IsCancellationRequested)
        {
            try
            {
                // Connect if not connected
                if (!_masters.ContainsKey(config.DeviceId))
                {
                    Connect(config);
                }

                // Read and publish values
                var values = _masters[config.DeviceId].ReadHoldingRegisters((byte)config.UnitId, (ushort)config.StartAddress, (ushort)config.Count);

                if (_lastValues.ContainsKey(config.DeviceId))
                {
                    var lastValues = _lastValues[config.DeviceId];
                    if (!values.SequenceEqual(lastValues))
                    {
                        _lastValues[config.DeviceId] = values;
                        PublishValues(config.DeviceId, values);
                    }
                }
                else
                {
                    _lastValues[config.DeviceId] = values;
                    PublishValues(config.DeviceId, values);
                }
            }
            catch (Exception ex)
            {
                LogError?.Invoke($"Device {config.DeviceId} error: {ex.Message}");
                OnConnectionLost?.Invoke(config.DeviceId, ex);
                Disconnect(config.DeviceId);
                await Task.Delay(config.RetryInterval);
            }

            await Task.Delay(config.PollInterval);
        }
    }

    // 设备连接方法（连接设备并初始化轮询）
    public void Connect(DeviceConfig config)
    {
        if (config.Protocol == "TCP")
        {
            var client = new TcpClient();
            client.Connect(config.Ip, config.Port);
            var factory = new ModbusFactory();
            var master = factory.CreateMaster(client);
            _masters[config.DeviceId] = master;
        }
        else if (config.Protocol == "RTU")
        {
            var port = new SerialPort(config.PortName, config.BaudRate, config.Parity, config.DataBits, config.StopBits)
            {
                ReadTimeout = config.ReadTimeout,
                WriteTimeout = config.WriteTimeout
            };
            port.Open();

            var factory = new ModbusFactory();
            var resource = new SerialPortResource(port);
            var master = factory.CreateRtuMaster(resource);
            _masters[config.DeviceId] = master;
        }
        else if (config.Protocol == "RTUOVERTCP")
        {
            var client = new TcpClient();
            client.Connect(config.Ip, config.Port);
            var _networkStream = client.GetStream();
            var factory = new ModbusFactory();
            var transport = new RtuOverTcpTransport(_networkStream); // RTU over TCP is based on RTU over TCP transport
            var master = factory.CreateRtuMaster(transport);
            _masters[config.DeviceId] = master;
        }

        _deviceConfigs[config.DeviceId] = config;

        // 启动轮询
        StartPolling(config);
    }


    private void Disconnect(string deviceId)
    {
        lock (_tcpClients)
        {
            if (_tcpClients.ContainsKey(deviceId))
            {
                _tcpClients[deviceId].Close();
                _tcpClients.Remove(deviceId);
            }
        }
        lock (_serialPorts)
        {
            if (_serialPorts.ContainsKey(deviceId))
            {
                _serialPorts[deviceId].Close();
                _serialPorts.Remove(deviceId);
            }
        }
        _masters.TryRemove(deviceId, out _);
        _lastValues.TryRemove(deviceId, out _);
    }

    private void PublishValues(string deviceId, ushort[] values)
    {
        if (_subscribers.ContainsKey(deviceId))
        {
            foreach (var callback in _subscribers[deviceId])
            {
                callback(values);
            }
        }
    }

    // 启动轮询功能：根据 PollInterval 设置定时器来定期读取数据
    private void StartPolling(DeviceConfig config)
    {
        var timer = new Timer(async _ =>
        {
            await PollDevice(config);
        }, null, 0, config.PollInterval); // 延迟 0 毫秒开始，第一个轮询立即执行，之后每隔 PollInterval 进行一次

        _pollingTimers[config.DeviceId] = timer;
    }

    // 停止轮询
    public void StopPolling(string deviceId)
    {
        if (_pollingTimers.ContainsKey(deviceId))
        {
            _pollingTimers[deviceId].Dispose();
            _pollingTimers.TryRemove(deviceId, out _);
        }
    }

    // 定期轮询设备并读取数据
    private async Task PollDevice(DeviceConfig config)
    {
        try
        {
            // 使用 Modbus 主站读取寄存器值
            var values = _masters[config.DeviceId].ReadHoldingRegisters((byte)config.UnitId, (ushort)config.StartAddress, (ushort)config.Count);

            // 发布读取的数据
            OnDataReceived?.Invoke(config.DeviceId, values);

            // 打印日志或执行其他操作
            LogInfo?.Invoke($"Device {config.DeviceId} read values: {string.Join(", ", values)}");
        }
        catch (Exception ex)
        {
            // 错误处理
            LogError?.Invoke($"Error reading device {config.DeviceId}: {ex.Message}");
        }
    }


}


